package com.lerays.spider.sougou.hack;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

/**
 * session factory:
 * 1. handler the data conmunication with the session target server. <br />
 * 2. offer interface to add the session. <br />
 * 3. offer interface to fetch a session. <br />
 * 
 * and it must be thread safe.
 * 
 * @author yuan<yumking14@gmail.com>
 * @version 2015.11.15
*/
public class SessionFactory 
{
	/**
	 * global http client manager
	*/
	private CloseableHttpClient httpClient = null;
	
	/**
	 * Session list 
	*/
	private List<Session> sessionList = null;
	
	/**
	 * global dispatch seed 
	*/
	private int seed = 0;
	
	/**
	 * session clear thread 
	*/
	private Thread thread = null;
	
	/**
	 * running state of the session factory 
	*/
	private volatile boolean running = false;
	
	/**
	 * contruct method to create a new session factory
	 * 
	 * @param	httpConnectionPoolSize
	 * @param	httpMaxRetryTimes
	*/
	public SessionFactory(int httpConnectionPoolSize, int httpMaxRetryTimes)
	{
		sessionList = Collections.synchronizedList(new ArrayList<Session>());
	    
		//set the connection pool size
		Registry<ConnectionSocketFactory> reg = 
				RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
		
		//create the PoolingHttpClient connection manager
		PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(reg);
        connectionManager.setDefaultMaxPerRoute(100);
		connectionManager.setMaxTotal(httpConnectionPoolSize);
		
		//Socket config
		SocketConfig socketConfig = SocketConfig.custom()
				.setSoKeepAlive(true)
				.setTcpNoDelay(true)
				.build();
		
		//http retry handler
		HttpRequestRetryHandler retryHandler = 
				new DefaultHttpRequestRetryHandler(httpMaxRetryTimes, true);
		
		httpClient = HttpClients.custom()
				.disableRedirectHandling()			//prevent the self redirect
				.setConnectionManager(connectionManager)
				.setDefaultSocketConfig(socketConfig)
				.setRetryHandler(retryHandler).build();
	}
	
	/**
	 * {@link #SessionFactory(int, int)} 
	*/
	public SessionFactory()
	{
		this(1000, 3);
	}
	
	/**create the PoolingHttpClient connection manager
	 * start the session factory 
	*/
	public SessionFactory start()
	{
		if ( thread != null )
		{
			return this;
		}
		
		running = true;
		thread = new Thread(new Runnable(){
			@Override
			public void run() 
			{
				while ( true ) 
				{
					if ( running == false )
					{
						break;
					}
					
					/**
					 * session clear thread
					 * check all of them every 5 seconds and remove the invalidate session
					*/
					synchronized ( sessionList )
					{
						Iterator<Session> it = sessionList.iterator();
						while ( it.hasNext() )
						{
							Session sess = it.next();
							if ( sess.getValidate() == false )
							{
								it.remove();
							}
						}
					}
					
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		thread.start();
		
		return this;
	}
	
	/**
	 * add a new client session 
	 * 
	 * @param	session
	*/
	public void add( Session session )
	{
		sessionList.add(session.start(httpClient));
//		try {
//                Thread.sleep(5000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//		clear();
	}
	
	/**
	 * get a specifield session by index
	 * 
	 * @param	idx
	 * @param	Session
	*/
	public Session get(int idx)
	{
		return sessionList.get(idx);
	}
	
	/**
	 * get the size of the session list
	 * 
	 * @return	int
	*/
	public int size()
	{
		return sessionList.size();
	}
	
	/**
	 * get a session randomly
	 * 
	 * @return	Session
	*/
	public Session getRandomly()
	{
		int seed = (int)(Math.random() * 1331331);
		return sessionList.get(seed % sessionList.size());
	}
	
	/**
	 * get a session polling
	 * 
	 * @return	Session
	*/
	public Session getPolling()
	{
		if ( (seed++) < 0 ) seed = 0;
		return sessionList.get(seed % sessionList.size());
	}
	
	/**
	 * return all the sessions
	 * 
	 * @return	List<Session>
	*/
	public List<Session> getList()
	{
		return sessionList;
	}
	
	/**
	 * clear the session
	 * */
	public void clear()  {
	       synchronized (sessionList)
	       {
	               sessionList.clear();
	       }
	}
	
	/**
	 * get the http client manager 
	 * 
	 * @return CloseableHttpClient
	*/
	public CloseableHttpClient getHttpClient()
	{
		return httpClient;
	}
	
	/**
	 * shutdown the current session factory 
	*/
	public void shutdown()
	{
		running = false;
		try {httpClient.close();} catch (IOException e) {e.printStackTrace();}
		sessionList.clear();
	}
}
